/* Copyright (c) 2007-2008, Jeffrey R Griffin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Jeffrey R Griffin nor the
*       names of his contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Jeffrey R Griffin ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package net.visibleblue.android.app.dynamic;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

import org.xmlpull.v1.XmlPullParser;

import android.app.Activity;
import android.app.ActivityThread;
import android.app.Application;
import android.content.AssetManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentReceiver;
import android.content.PackageManager;
import android.content.Resources;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.PackageManager.NameNotFoundException;
import android.content.Resources.Theme;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.net.ContentURI;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewInflate;
import android.view.Window;
import android.view.WindowManager;
import android.view.Menu.Item;
import android.view.WindowManager.LayoutParams;
import android.widget.WidgetInflate;

/**
 * Extending DynamicActivity functions much in the same way as inheriting
 * Activity.  A class extending DynamicActivity can be encapsulated in an Activity
 * class, such as DynamicActivityHost and will handle UI, XML layout de-serialization,
 * resource manipulation, life cycle events, key events, menu events, etc.
 * 
 * Note: At this time I have left out sub-activity functionality.  If I can work it
 * in a friendly way I will do so in time.  That said, under the use case I am observing
 * for this framework it may be desirable to deploy components of complex applications
 * in separate packages, so that each component can be downloaded in a negligible amount
 * of time.
 *
 */
public abstract class DynamicActivity extends Context {
	
	private ActivityThread.PackageInfo _packageInfo = null;
	private ClassLoader _classLoader = null;
	private String _packageName = null;
	private String _packagePath = null;
	private Resources _resources = null;
	private Activity _host = null;
	
	public CharSequence getTitle()
	{
		return null;
	}
	
	public int getThemeResource()
	{
		return -1;
	}
	
	public Intent getIntent()
	{
		return _host.getIntent();
	}
	
	protected ClassLoader getPackageClassLoader()
	{
		return DynamicActivityHost._urlApkPM.getClassLoader();
	}
	
	/**
	 * The inject method allows the host activity to populate the necessary 
	 * DynamicActivity members.
	 */
	public void inject(Activity host, String packageName, String packagePath, 
			ClassLoader loader){
		_host = host;
		_packageName = packageName;
		_packagePath = packagePath;
		_classLoader = loader;
		_packageInfo = new ActivityThread.PackageInfo(ActivityThread.currentActivityThread(), 
				_packageName, _packagePath, _host.getDataDir(), ActivityThread.currentActivityThread(), 
				_classLoader, false, true);
		_resources = _packageInfo.getResources(ActivityThread.currentActivityThread());
		/*_host.setTitle(_resources.getIdentifier(_packageName+":string/app_name", 
				"string", _packageName));*/
	}

	@Override
	public boolean bindService(Intent service, String interfaceName,
			ServiceConnection conn, int flags) {
		return _host.bindService(service, interfaceName, conn, flags);
	}

	@Override
	public void broadcastIntent(Intent intent) {
		_host.broadcastIntent(intent);
	}

	@Override
	public void broadcastIntent(Intent intent, String receiverPermission) {
		_host.broadcastIntent(intent, receiverPermission);
	}

	@Override
	public void broadcastIntent(Intent intent, String receiverPermission,
			IntentReceiver resultReceiver, Handler scheduler, int initialCode,
			String initialData, Bundle initialExtras) {
		_host.broadcastIntent(intent, receiverPermission, resultReceiver, scheduler, 
				initialCode, initialData, initialExtras);
	}

	@Override
	public int checkCallingOrSelfPermission(String permission) {
		return _host.checkCallingOrSelfPermission(permission);
	}

	@Override
	public int checkCallingPermission(String permission) {
		return _host.checkCallingPermission(permission);
	}

	@Override
	public int checkPermission(String permission, int pid, int uid) {
		return _host.checkPermission(permission, pid, uid);
	}

	@Override
	public void clearWallpaper() {
		_host.clearWallpaper();
	}

	@Override
	public SQLiteDatabase createDatabase(String name, int version, int mode,
			CursorFactory factory) throws FileNotFoundException {
		return _host.createDatabase(name, version, mode, factory);
	}

	@Override
	public Context createPackageContext(String packageName, int flags)
			throws NameNotFoundException {
		return _host.createPackageContext(packageName, flags);
	}

	@Override
	public boolean deleteDatabase(String name) {
		return _host.deleteDatabase(name);
	}

	@Override
	public boolean deleteFile(String name) {
		return _host.deleteFile(name);
	}

	@Override
	public String[] fileList() {
		return _host.fileList();
	}

	@Override
	public AssetManager getAssets() {
		return _resources.getAssets();
	}

	@Override
	public ClassLoader getClassLoader() {
		return _classLoader;
	}

	@Override
	public ContentResolver getContentResolver() {
		return _host.getContentResolver();
	}

	@Override
	public String getDataDir() {
		return _host.getDataDir();
	}

	@Override
	public File getFileStreamPath(String name) throws FileNotFoundException {
		return _host.getFileStreamPath(name);
	}

	@Override
	public PackageManager getPackageManager() {
		return _host.getPackageManager();
	}

	@Override
	public String getPackageName() {
		return _packageName;
	}

	@Override
	public String getPackagePath() {
		return _packagePath;
	}

	@Override
	public Resources getResources() {
		return _resources;
	}

	@Override
	public SharedPreferences getSharedPreferences(String name, int mode) {
		return _host.getSharedPreferences(name, mode);
	}

	@Override
	public Object getSystemService(String name) {
		return _host.getSystemService(name);
	}

	@Override
	public Theme getTheme() {
		return _host.getTheme();
	}

	@Override
	public Drawable getWallpaper() {
		return _host.getWallpaper();
	}

	@Override
	public SQLiteDatabase openDatabase(String file, CursorFactory factory)
			throws FileNotFoundException {
		return _host.openDatabase(file, factory);
	}

	@Override
	public FileInputStream openFileInput(String name)
			throws FileNotFoundException {
		return _host.openFileInput(name);
	}

	@Override
	public FileOutputStream openFileOutput(String name, int mode)
			throws FileNotFoundException {
		return _host.openFileOutput(name, mode);
	}

	@Override
	public Drawable peekWallpaper() {
		return _host.peekWallpaper();
	}

	@Override
	public Intent registerReceiver(IntentReceiver receiver, IntentFilter filter) {
		return _host.registerReceiver(receiver, filter);
	}

	@Override
	public Intent registerReceiver(IntentReceiver receiver,
			IntentFilter filter, String broadcastPermission, Handler scheduler) {
		return _host.registerReceiver(receiver, filter, broadcastPermission, scheduler);
	}

	@Override
	public void setTheme(int resid) {
		_host.setTheme(resid);
	}

	@Override
	public void setWallpaper(Bitmap bitmap) throws IOException {
		_host.setWallpaper(bitmap);
	}

	@Override
	public void setWallpaper(InputStream data) throws IOException {
		_host.setWallpaper(data);
	}

	@Override
	public DialogInterface showAlert(CharSequence title, CharSequence message,
			CharSequence buttonText, boolean cancelable) {
		return _host.showAlert(title, message, buttonText, cancelable);
	}

	@Override
	public DialogInterface showAlert(CharSequence title, CharSequence message,
			CharSequence buttonText, OnClickListener buttonListener,
			boolean cancelable, OnCancelListener cancelListener) {
		return _host.showAlert(title, message, buttonText, buttonListener, 
				cancelable, cancelListener);
	}

	@Override
	public DialogInterface showAlert(CharSequence title, CharSequence message,
			CharSequence button1Text, OnClickListener button1Listener,
			CharSequence button2Text, OnClickListener button2Listener,
			boolean cancelable, OnCancelListener cancelListener) {
		return _host.showAlert(title, message, button1Text, button1Listener,
				button2Text, button2Listener, cancelable, cancelListener);
	}

	@Override
	public DialogInterface showAlert(CharSequence title, CharSequence message,
			CharSequence button1Text, Message button1Callback,
			CharSequence button2Text, Message button2Callback,
			boolean cancelable, Message cancelCallback) {
		return _host.showAlert(title, message, button1Text, button1Callback, 
				button2Text, button2Callback, cancelable, cancelCallback);
	}

	@Override
	public void startActivity(Intent intent) {
		_host.startActivity(intent);
	}
	
	public void startSubActivity(Intent intent, int requestCode) {
		_host.startSubActivity(intent, requestCode);
	}

	@Override
	public boolean startInstrumentation(ComponentName className,
			String profileFile, Bundle arguments) {
		return _host.startInstrumentation(className, profileFile, arguments);
	}

	@Override
	public ComponentName startService(Intent service, Bundle args) {
		return _host.startService(service, args);
	}

	@Override
	public boolean stopService(Intent service) {
		return _host.stopService(service);
	}

	@Override
	public void unbindService(ServiceConnection conn) {
		_host.unbindService(conn);
	}

	@Override
	public void unregisterReceiver(IntentReceiver arg0) {
		_host.unregisterReceiver(arg0);
	}
	
	protected void onCreate(Bundle icicle) {
    }

	protected void applyThemeResource(Theme theme, int resid, boolean first) {
	}

	public boolean dispatchKeyEvent(KeyEvent event) {
		return false;
	}

	protected void onCompleteThaw(Bundle state) {
	}

	public void onContentChanged() {
	}

	public CharSequence onCreateDescription() {
		return null;
	}

	public boolean onCreateOptionsMenu(Menu menu) {
		return false;
	}

	public boolean onCreatePanelMenu(int featureId, Menu menu) {
		return false;
	}

	public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas) {
		return false;
	}

	protected void onDestroy() {
	}

	protected void onFreeze(Bundle outState) {
	}

	public boolean onKeyDown(int keyCode, KeyEvent event) {
		return false;
	}

	public boolean onKeyUp(int keyCode, KeyEvent event) {
		return false;
	}

	protected void onNewIntent(Intent intent) {
	}

	public boolean onOptionsItemSelected(Item item) {
		return false;
	}

	protected void onPause() {
	}

	protected void onPostCreate(Bundle icicle) {
	}

	protected void onPostResume() {
	}

	public boolean onPrepareOptionsMenu(Menu menu) {
		return false;
	}

	protected void onResume() {
	}

	protected void onRestart() {
	}

	protected void onStart() {
	}

	protected void onStop() {
	}

	public void onWindowAttributesChanged(LayoutParams params) {
	}

	public void onWindowFocusChanged(boolean hasFocus) {
	}
	
	public void addContentView(View view, ViewGroup.LayoutParams params){
		_host.addContentView(view, params);
	}

	public View createView(String name, AttributeSet attrs, Map inflateParams){
		return _host.createView(name, attrs, inflateParams);
	}
	
	public View findViewById(int id){
		return _host.findViewById(id);
	}

	public void finish(){
		_host.finish();
	}
	
	public final Application getApplication(){
		return _host.getApplication();
	}
	
	public SharedPreferences getPreferences(int mode){
		return _host.getPreferences(mode);
	}

	public int getTaskId(){
		return _host.getTaskId();
	}
	
	public int getTitleColor(){
		return _host.getTitleColor();
	}
	
	public Window getWindow(){
		return _host.getWindow();
	}
	
	public WindowManager getWindowManager(){
		return _host.getWindowManager();
	}
	
	public boolean isFinishing(){
		return _host.isFinishing();
	}
	
	public boolean isTaskRoot(){
		return _host.isTaskRoot();
	}
	
	public void managedCommitUpdates(Cursor c){
		_host.managedCommitUpdates(c);
	}
	
	public final Cursor managedQuery(ContentURI uri, String[] projection, 
			String selection, String[] selectionArgs, String sortOrder){
		return _host.managedQuery(uri, projection, selection, sortOrder);
	}
	
	public boolean moveTaskBackwards(boolean nonRoot){
		return _host.moveTaskBackwards(nonRoot);
	}

	public boolean moveTaskToBack(boolean nonRoot){
		return _host.moveTaskToBack(nonRoot);
	}
	
 	public final boolean requestWindowFeature(int featureId){
 		return _host.requestWindowFeature(featureId);
 	}
 	
	public void setContentView(View view, LayoutParams params){
		_host.setContentView(view, params);
	}
	
	public void setContentView(View view){
		_host.setContentView(view);
	}
	
	public void setContentView(int layoutResID){
		View view = _host.findViewById(layoutResID);
		if(view==null)
		{
			XmlPullParser layoutParser = getResources().getLayout(layoutResID);
			view = getWindow().getViewInflate().inflate(layoutParser, null, null);
		}
		_host.setContentView(view);
	}
	
	public final void setDefaultKeyMode(int mode){
		_host.setDefaultKeyMode(mode);
	}
	
	public final void setFeatureDrawable(int featureId, Drawable drawable){
		_host.setFeatureDrawable(featureId, drawable);
	}
	
	public final void setFeatureDrawableAlpha(int featureId, int alpha){
		_host.setFeatureDrawableAlpha(featureId, alpha);
	}
	
	public final void setFeatureDrawableResource(int featureId, int resId){
		_host.setFeatureDrawable(featureId, getResources().getDrawable(resId));
	}
	
	public final void setFeatureDrawableUri(int featureId, ContentURI uri){
		_host.setFeatureDrawableUri(featureId, uri);
	}
	
	public void setIntent(Intent newIntent){
		_host.setIntent(newIntent);
	}
	
	public void setPersistent(boolean isPersistent){
		_host.setPersistent(isPersistent);
	}
		
	public final void setResult(int resultCode, String data){
		_host.setResult(resultCode, data);
	}
	
	public final void setResult(int resultCode, String data, Bundle extras){
		_host.setResult(resultCode, data, extras);
	}
	
	public final void setResult(int resultCode){
		_host.setResult(resultCode);
	}
	
	public void setTitle(int titleId){
		_host.setTitle(titleId);
	}
	
	public void setTitle(CharSequence title){
		_host.setTitle(title);
	}
	
	public void setTitleColor(int textColor){
		_host.setTitleColor(textColor);
	}
  	
	public void startManagingCursor(Cursor c){
		_host.startManagingCursor(c);
	}
	
	public void stopManagingCursor(Cursor c){
		_host.stopManagingCursor(c);
	}
	
	public void takeKeyEvents(boolean get){
		_host.takeKeyEvents(get);
	}
	
	protected boolean isFullscreenOpaque(){
		boolean result = false;
		try
		{
		Method isFSOMethod = Activity.class.getMethod("isFullscreenOpaque", 
				new Class[]{});
		result = ((Boolean)isFSOMethod.invoke(_host, new Object[]{})).booleanValue();
		}
		catch(NoSuchMethodException nsmEx){}
		catch(InvocationTargetException invEx){}
		catch(IllegalAccessException illEx){}
		return result;
	}
}
